# -*- coding: utf-8 -*-
# 新帧率.py was created by RuanYaoHuang on 2023/5/29.

# -*- coding: utf-8 -*-

import os
import cv2
import time
import queue
import serial
import string
import ctypes
import tiffile
import threading
import numpy as np
from datetime import datetime
import serial.tools.list_ports

from ASI.ASI_CAMERA_INFO import ASI_CAMERA_INFO
from ASI.ASI_ERROR_CODE import ASI_ERROR_CODE
from ASI.ASI_IMG_TYPE import ASI_IMG_TYPE
from ASI.ASI_CONTROL_TYPE import ASI_CONTROL_TYPE


# 加载SDK
sdk = ctypes.cdll.LoadLibrary("../ASICamera2.dll")
# 获取连接的个数
sdk.ASIGetNumOfConnectedCameras.restype = ctypes.c_int
sdk.ASIGetNumOfConnectedCameras.argtypes = []
# 获取相机属性
sdk.ASIGetCameraProperty.restype = ASI_ERROR_CODE
sdk.ASIGetCameraProperty.argtypes = [ctypes.POINTER(ASI_CAMERA_INFO), ctypes.c_int]
# 打开相机
sdk.ASIOpenCamera.restype = ctypes.c_int
sdk.ASIOpenCamera.argtypes = [ctypes.c_int]
# 初始化相机
sdk.ASIInitCamera.restype = ctypes.c_int
sdk.ASIInitCamera.argtypes = [ctypes.c_int]
# 设置控制相机的参数
sdk.ASISetControlValue.restype = ASI_ERROR_CODE
sdk.ASISetControlValue.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_long, ctypes.c_int]
# 查看控制相机的参数
sdk.ASIGetControlValue.restype = ASI_ERROR_CODE
sdk.ASIGetControlValue.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_long),
								   ctypes.POINTER(ctypes.c_int)]
# 定义相机的拍照格式
sdk.ASISetROIFormat.restype = ctypes.c_int
sdk.ASISetROIFormat.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int]
# 获取相机的拍照格式
sdk.ASIGetROIFormat.restype = ASI_ERROR_CODE
sdk.ASIGetROIFormat.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_long), ctypes.POINTER(ctypes.c_long),
								ctypes.POINTER(ctypes.c_long), ctypes.POINTER(ctypes.c_long)]
# 开始录制视频
sdk.ASIStartVideoCapture.restype = ASI_ERROR_CODE
sdk.ASIStartVideoCapture.argtypes = [ctypes.c_int]
# 获取录制视频后的图像
sdk.ASIGetVideoData.restype = ASI_ERROR_CODE
sdk.ASIGetVideoData.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_ubyte), ctypes.c_long, ctypes.c_int]
# 结束录制视频
sdk.ASIStopVideoCapture.restype = ASI_ERROR_CODE
sdk.ASIStopVideoCapture.argtypes = [ctypes.c_int]
# 关闭相机
sdk.ASICloseCamera.restype = ASI_ERROR_CODE
sdk.ASICloseCamera.argtypes = [ctypes.c_int]

# 获取盘符余量
def get_free_space(path):
	free_bytes = ctypes.c_ulonglong(0)
	ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(path), None, None, ctypes.pointer(free_bytes))
	return free_bytes
# 获取参数数值
def get_param_value(came: int, parament: int):
	_param = ctypes.c_long()
	_status = ctypes.c_int()
	sdk.ASIGetControlValue(came,
						   parament,
						   ctypes.byref(_param),
						   ctypes.byref(_status))
	return _param.value, _status.value
# 设置参数数值
def set_param_value(came: int, parament: int, _param: ctypes.c_long, _status: ctypes.c_int):
	ret = sdk.ASISetControlValue(came, parament, _param, _status)
	return ret


# 1、获取连接个数
def acquire_connect_num():
	# 获取连接的个数
	num_cameras = sdk.ASIGetNumOfConnectedCameras()
	print(f"Step1: Number of connected cameras: {num_cameras}")

# 2、开启相机
def open_camera():
	# 打开相机
	sdk.ASIOpenCamera.restype = ctypes.c_int
	sdk.ASIOpenCamera.argtypes = [ctypes.c_int]
	came = ctypes.c_int()
	ret = sdk.ASIOpenCamera(came)
	if ret != 0:
		print("Failed to get camera ID")
	else:
		print(f"Step2: Open the Camera, Camera ID is: {came.value}")
	return came

# 3、设置参数自动并设置RIO
def set_camera_param(came):
	initial_camera = sdk.ASIInitCamera(came.value)
	if initial_camera == 0:
		# 3-1 开启自动曝光
		exp_param, exp_status = get_param_value(came.value, ASI_CONTROL_TYPE.ASI_EXPOSURE)
		print(f'    expourse param value is {exp_param}, status is {exp_status}')
		gain_param, gain_status = get_param_value(came.value, ASI_CONTROL_TYPE.ASI_GAIN)
		print(f'    gain param value is {gain_param}, status is {gain_status}')
		set_param_value(came.value, ASI_CONTROL_TYPE.ASI_EXPOSURE, ctypes.c_long(exp_param), ctypes.c_int(1))
		set_param_value(came.value, ASI_CONTROL_TYPE.ASI_GAIN, ctypes.c_long(gain_param), ctypes.c_int(1))

		# 3-2 获取分辨率和设置位深
		cam_info = ASI_CAMERA_INFO()
		sdk.ASIGetCameraProperty(ctypes.byref(cam_info), came.value)

		sdk.ASISetROIFormat(came.value,
							ctypes.c_int(cam_info.MaxWidth), ctypes.c_int(cam_info.MaxHeight),
							1,
							ctypes.c_int(ASI_IMG_TYPE.ASI_IMG_RAW16))

		# 查看相机的拍照格式
		w = ctypes.c_long()
		h = ctypes.c_long()
		b = ctypes.c_long()
		t = ctypes.c_long()
		sdk.ASIGetROIFormat(came.value,
							ctypes.byref(w), ctypes.byref(h),
							ctypes.byref(b),
							ctypes.byref(t))
		if w.value == cam_info.MaxWidth and h.value == cam_info.MaxHeight and t.value == ASI_IMG_TYPE.ASI_IMG_RAW16:
			print("Camera RIOFormat set Succeed!")
			return h.value, w.value, t.value
		else:
			print("Camera RIOFormat set Failed")
			return
	else:
		print("Failed to initialize the SDK")
	return

# 4、开启视频并自动调节固定帧数后关闭
def action_set(came, output_high, output_width, frames):
	# 开始录制视频
	sdk.ASIStartVideoCapture(came.value)

	buffer_size = int(output_width) * int(output_high) * 2
	buffer = (ctypes.c_ubyte * buffer_size)()
	waits = ctypes.c_int(1000)
	twice = 1
	start_time = time.time()
	exp, gain = None, None
	frames_rate = 0
	while twice < frames:
		exp, _ = get_param_value(came.value, ASI_CONTROL_TYPE.ASI_EXPOSURE)
		gain, _ = get_param_value(came.value, ASI_CONTROL_TYPE.ASI_GAIN)
		sdk.ASIGetVideoData(came.value, buffer, buffer_size, waits)
		np.frombuffer(buffer, dtype='uint16')
		if twice > frames:
			break
		elapsed_time = time.time() - start_time
		frames_rate = round(twice / elapsed_time, 2)
		twice += 1
		print(f"I'm running {twice}. exp {exp}, gain {gain}, fps {frames_rate}.")

	set_param_value(came.value, ASI_CONTROL_TYPE.ASI_EXPOSURE, ctypes.c_long(exp), ctypes.c_int(0))
	set_param_value(came.value, ASI_CONTROL_TYPE.ASI_GAIN, ctypes.c_long(gain), ctypes.c_int(0))

	_, exp_stat = get_param_value(came.value, ASI_CONTROL_TYPE.ASI_EXPOSURE)
	_, gain_stat = get_param_value(came.value, ASI_CONTROL_TYPE.ASI_GAIN)
	print(f'exp_status:{exp_stat}, gain_status:{gain_stat}')
	# 结束录制视频
	sdk.ASIStopVideoCapture(came.value)
	return frames_rate

# 5、开始视频录制线程
def action_video_fun(came, output_high, output_width, queue):
	# 开始录制视频
	sdk.ASIStartVideoCapture(came.value)
	buffer_size = int(output_width) * int(output_high) * 2
	buffer = (ctypes.c_ubyte * buffer_size)()
	waits = ctypes.c_int(10000)
	# start_time = time.time()
	# frames_rate = 0
	# twice = 1
	while True:
		sdk.ASIGetVideoData(came.value, buffer, buffer_size, waits)

		video_image = np.frombuffer(buffer, dtype='uint16')
		video_image = video_image.reshape((output_high, output_width))
		queue.put(video_image)
		# elapsed_time = time.time() - start_time
		# frames_rate = round(twice / elapsed_time, 2)
		# print(f"fps {frames_rate}.")
		# twice += 1
	# 结束录制视频
	sdk.ASIStopVideoCapture(came.value)

def _calculate_fps(buf):
	frame_count = 0
	last_time = time.time()
	while True:
		frame = buf.get()
		frame_count += 1
		elapsed_time = time.time() - last_time
		if elapsed_time > 1:
			fps = frame_count / elapsed_time
			frame_count = 0
			last_time = time.time()
			print(f"FPS: {fps:.2f}")

if __name__ == '__main__':
	# 1、获取连接个数
	acquire_connect_num()
	# 2、开启相机
	camera_id = open_camera()
	# 3、设置参数自动并设置RIO
	image_high, image_width, image_type = set_camera_param(camera_id)
	# 4、开启视频并自动调节固定帧数后关闭
	frame_rate = action_set(camera_id, image_high, image_width, 50)
	video_queue = queue.Queue()
	# 5、开始视频录制线程
	video_thread = threading.Thread(target=action_video_fun, args=(camera_id,
																   image_high, image_width,
																   video_queue,))

	video_thread.start()

	_calculate_fps(video_queue)
	video_thread.join()


